/**
 * \brief  Framebuffer driver front-end
 * \author Norman Feske
 * \author Christian Helmuth
 * \author Sebastian Sumpf
 * \date   2007-09-11
 */

/*
 * Copyright (C) 2007-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

/* Genode */
#include <base/env.h>
#include <base/sleep.h>
#include <root/component.h>
#include <cap_session/connection.h>
#include <framebuffer_session/server.h>

/* Local */
#include "framebuffer.h"

using namespace Genode;


/*****************
 ** C++ support **
 *****************/

/**
 * Anonymous new operator
 */
void *operator new (size_t size)
{
	return env()->heap()->alloc(size);
}


/***********************************************
 ** Implementation of the framebuffer service **
 ***********************************************/

/*
 * Screen configuration
 *
 * default video mode is 640x480@16
 */
static unsigned long scr_width = 1024, scr_height = 768, scr_mode = 16;

namespace Framebuffer {

	class Session_component : public Session_server
	{
		public:

			Dataspace_capability dataspace() { return Framebuffer_drv::hw_framebuffer(); }

			void info(int *out_w, int *out_h, Mode *out_mode)
			{
				*out_w = scr_width;
				*out_h = scr_height;

				switch (scr_mode) {
				case 16: *out_mode = RGB565; break;
				default: *out_mode = INVALID;
				}
			}

			/* not implemented */
			void refresh(int x, int y, int w, int h){ }
	};


	class Root : public Root_component<Session_component>
	{
		protected:

			Session_component *_create_session(const char *args)
			{
				scr_width  = Arg_string::find_arg(args,  "fb_width").ulong_value(scr_width);
				scr_height = Arg_string::find_arg(args, "fb_height").ulong_value(scr_height);
				scr_mode   = Arg_string::find_arg(args,   "fb_mode").ulong_value(scr_mode);

				if (Framebuffer_drv::set_mode(scr_width, scr_height, scr_mode)) {
					PWRN("Could not set vesa mode %lux%lu@%lu", scr_width, scr_height,
					     scr_mode);
					throw Root::Invalid_args();
				}

				return new (md_alloc()) Session_component(); }

		public:

			Root(Server_entrypoint *session_ep, Allocator *md_alloc)
			: Root_component<Session_component>(session_ep, md_alloc) { }
	};
}


int main(int argc, char **argv)
{
	/* We need to create capabilities for sessions. Therefore, we request the
	 * CAP service. */
	static Cap_connection cap;

	/* initialize server entry point */
	static Msgbuf<256> snd_msg, rcv_msg;
	Server_activation<4096> *sa = new (env()->heap())
	                              Server_activation<4096>(&snd_msg, &rcv_msg);
	Server_entrypoint       *e  = new (env()->heap())
	                              Server_entrypoint(&cap, sa);

	/* init driver back-end */
	if (Framebuffer_drv::init()) {
		PERR("H/W driver init failed");
		return 3;
	}

	/* entry point serving framebuffer root interface */
	Framebuffer::Root *framebuffer_root  = new (env()->heap())
	                                       Framebuffer::Root(e, env()->heap());
	Root_capability framebuffer_root_cap = Root_capability(e->manage(framebuffer_root));

	/* tell parent about the service */
	env()->parent()->announce(Framebuffer::Session::service_name(), framebuffer_root_cap);

	/* main's done - go to sleep */

	sleep_forever();
	return 0;
}
